#include "pch.hpp"
#include "TPhysicsDevice.hpp"
#include "TPhysicsException.hpp"




TPhysicsDevice::TPhysicsDevice(const EngineInfo &info)
	: m_logger(info.serviceProvider->service<Logger>())
{
	m_logger->log_info("Initializing physics system: Fysics", __FILE__, __FUNCTION__, __LINE__);

	m_logger->log_info("physics system completely initialized", __FILE__, __FUNCTION__, __LINE__);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

TPhysicsDevice::~TPhysicsDevice()
{
	if(m_actors.size())
		BOOSTEXT_THROW(boostext::programming_error("Not all actors have been released"));
}
///////////////////////////////////////////////////////////////////////////////////////////////////




QString TPhysicsDevice::apiName() const
{
	return "Taleon Physics";
}
///////////////////////////////////////////////////////////////////////////////////////////////////




void TPhysicsDevice::update()
{
	// If the timer has not started yet, we assume a perfect 60 hz simulation in the first frame
	// Otherwise we just use the elapsed time since the last frame
	int64   time = m_timer.elapsed(); m_timer.reset();
	float_t dt   = time / float_t(1000000);


	//////////////////////
	// Step 1, Dynamics //
	//////////////////////

	foreach(PhysicsActor* actor, m_actors)
	{
		static_cast<TPhysicsActor*>(actor)->update(dt);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////




PhysicsDevice::RayCastResult TPhysicsDevice::rayCast(const ray& ray_) const
{
	RayCastResult result;
	// This first version uses a brute force version

	// Look for the actor who's hitpoint turns out to be the nearest
	// at the ray's start (we can check the parameter for simplicity).
	foreach(PhysicsActor* actor, m_actors)
	{
		PhysicsActor::RayCastResult tmp = actor->rayCast(ray_);
		if(tmp && !result || tmp->parameter < result->parameter)
		{
			result = SceneRayCast();
			result->actor = actor->ptr();
			result->parameter = tmp->parameter;
			result->position  = tmp->position;
			result->normal    = tmp->normal;
		}
	}

	return result;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void TPhysicsDevice::debugRender(MeshVertices& vertices, Indices32 &indices) const
{
	vertices.clear();
	indices.clear();

	foreach(const PhysicsActor* actor, m_actors)
	{
		actor->debugRender(vertices, indices);
	}
}
///////////////////////////////////////////////////////////////////////////////////////////////////




PhysicsActorPtr TPhysicsDevice::createActor(const ActorDesc& desc)
{
	PhysicsActorPtr actor(new TPhysicsActor(desc, boost::bind(&TPhysicsDevice::removeActor, this, _1)));
	m_actors.insert(actor.get());
	return actor;
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void TPhysicsDevice::removeActor(PhysicsActor* actor)
{
	Actors::iterator i = m_actors.find(static_cast<PhysicsActor*>(actor));
	if(i == m_actors.end())
		BOOSTEXT_THROW(programming_error("The actor does not exist and cannot be removed"));

	m_actors.erase(i);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
